package cn.iocoder.yudao.module.ai.service.jupyter;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONObject;
import cn.iocoder.yudao.module.ai.dal.dataobject.busimodelparam.BusiModelParamDO;
import cn.iocoder.yudao.module.ai.utils.DockerClient;
import cn.iocoder.yudao.module.infra.controller.admin.redis.RedisController;
import cn.iocoder.yudao.module.infra.util.DateUtils;
import cn.iocoder.yudao.module.infra.util.StringUtils;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisConnectionUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.ServerSocket;
import java.nio.file.*;
import java.util.*;
import java.util.stream.Stream;

import cn.iocoder.yudao.module.ai.controller.admin.jupyter.vo.*;
import cn.iocoder.yudao.module.ai.dal.dataobject.jupyter.JupyterDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;

import cn.iocoder.yudao.module.ai.dal.mysql.jupyter.JupyterMapper;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;

/**
 * jupyter容器管理 Service 实现类
 *
 * @author gt
 */
@Service
@Validated
@Slf4j
public class JupyterServiceImpl implements JupyterService {

    @Resource
    private JupyterMapper jupyterMapper;

    @Resource
    private DockerClient dockerClient;

    @Value("${jupyter.work-path}")
    private String workPath;

    @Value("${jupyter.start-port}")
    private Integer startPort;

    @Value("${jupyter.end-port}")
    private Integer endPort;

    @Override
    public JupyterDO createJupyter(JupyterSaveReqVO createReqVO) {
        log.info("开始创建一个新的Jupyter容器。");
        int port = getNotUsedPort();
        if (port == -1) {
            log.info("暂未找到可用的空闲端口,请稍后再创建。");
        } else {
            log.info("新容器分配端口为: {} ", port);
        }
        JupyterDO jupyter = BeanUtils.toBean(createReqVO, JupyterDO.class);
        jupyter.setAutoCloseTime(DateUtils.getLocalDateTimeAfterMinutes(createReqVO.getExpirationTime()));
        jupyter.setOpenPort(port);
        String workFile = workPath + File.separator + UUID.randomUUID();
        log.info("容器工作路径为: {}", workFile);
        if (!new File(workFile).exists()) {
            new File(workFile).mkdirs();
        }
        jupyter.setWorkPath(workFile);
        jupyterMapper.insert(jupyter);
        jupyter.setJupyterName("jupyter_" + jupyter.getJupyterId());
        jupyterMapper.updateById(jupyter);
        return jupyter;
    }

    @Override
    public void updateJupyter(JupyterSaveReqVO updateReqVO) {
        // 更新
        JupyterDO updateObj = BeanUtils.toBean(updateReqVO, JupyterDO.class);
        jupyterMapper.updateById(updateObj);
    }

    @Override
    public void updateById(JupyterDO jupyter) {
        // 更新
        jupyterMapper.updateById(jupyter);
    }

    @Override
    public void deleteJupyter(Integer id) {
        // 删除
        jupyterMapper.deleteById(id);
    }


    @Override
    public JupyterDO getJupyter(Integer id) {
        return jupyterMapper.selectById(id);
    }

    @Override
    public PageResult<JupyterDO> getJupyterPage(JupyterPageReqVO pageReqVO) {
        return jupyterMapper.selectPage(pageReqVO);
    }

    //找一个未被Jupyter占用的合法端口
    private int getNotUsedPort() {
        Random random = new Random();
        //随机一个端口作为初始端口
        int initPort = startPort + random.nextInt((endPort - startPort) + 1);
        for (int i = initPort; i <= endPort; i++) {
            if (!isPortInUse(i)) {
                return i;
            }
        }
        for (int i = initPort; i >= startPort; i--) {
            if (!isPortInUse(i)) {
                return i;
            }
        }

        return -1;
    }

    //端口被占用
    public static boolean isPortInUse(int port) {
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            // 如果能成功绑定到这个端口，说明端口未被占用
            serverSocket.close();
            return false;
        } catch (IOException e) {
            // 如果抛出IOException，说明端口可能已被占用或存在其他问题
            return true;
        }
    }


    @Override
    public Path findConfigFileWithWalk(Path startPath, String fileName) throws IOException {
        try (Stream<Path> paths = Files.walk(startPath)) {
            return paths.filter(Files::isRegularFile)
                    .filter(path -> path.getFileName().toString().equals(fileName))
                    .findFirst()
                    .orElse(null);
        }
    }

    @Override
    public List<BusiModelParamDO> parseEnvFile(String filePath) throws IOException {
        List<BusiModelParamDO> params = new ArrayList<>();
        try (BufferedReader br = Files.newBufferedReader(Paths.get(filePath))) {
            String line;
            while ((line = br.readLine()) != null) {
                line = line.trim();
                if (!line.isEmpty() && !line.startsWith("#")) { //忽略注释
                    String[] parts = line.split("=");
                    if (parts.length == 2) {
                        String name = parts[0].trim();
                        String rawValue = parts[1].trim();
                        Object value;
                        String type;
                        if (rawValue.startsWith("\"") && rawValue.endsWith("\"")) {
                            value = rawValue.substring(1, rawValue.length() - 1);
                            type = "string";
                        } else if (rawValue.matches("-?\\d+")) {
                            value = Integer.parseInt(rawValue);
                            type = "int";
                        } else if (rawValue.startsWith("{") && rawValue.endsWith("}")) {
                            value = rawValue;
                            type = "object";
                        }else if (rawValue.startsWith("[") && rawValue.endsWith("]")) {
                            value = rawValue;
                            type = "array";
                        } else {
                            value = rawValue;
                            type = "string";
                        }
                        params.add(new BusiModelParamDO(name, value.toString(), type));
                    }
                }
            }
        }
        return params;
    }


    @Override
    public List<BusiModelParamDO> parseSceneFile(String filePath) throws IOException {
        List<BusiModelParamDO> params = new ArrayList<>();
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            File file = new File(filePath);
            String jsonContent = new String(Files.readAllBytes(file.toPath()));
            JsonNode rootNode = objectMapper.readTree(jsonContent);
            Iterator<Map.Entry<String, JsonNode>> fields = rootNode.fields();
            while (fields.hasNext()) {
                Map.Entry<String, JsonNode> field = fields.next();
                String name = field.getKey();
                JsonNode valueNode = field.getValue();
                Object value;
                String type;
                if (valueNode.isTextual()) {
                    value = valueNode.asText();
                    type = "string";
                } else if (valueNode.isInt()) {
                    value = valueNode.asInt();
                    type = "int";
                } else if (valueNode.isArray()) {
                    value = valueNode.toPrettyString();
                    type = "array";
                } else if (valueNode.isObject()) {
                    value = valueNode.toPrettyString();
                    type = "object";
                } else {
                    value = valueNode.toString();
                    type = "string";
                }
                BusiModelParamDO envParam = new BusiModelParamDO(name, value.toString(), type);
                params.add(envParam);
            }
        } catch (IOException e) {
            e.printStackTrace();
            log.info("解析scene.conf文件发生异常 : {}",e.getMessage());
        }
        return params;
    }

    @Scheduled(fixedRate = 60000) // 每60秒（即每分钟）执行一次,检查并关闭多余的docker
    public void executeTask() {
        List<JupyterDO> jupyterDOList = queryExpiredContainers();
        jupyterDOList.forEach(jupyterDO -> {
            if(StringUtils.isNotEmpty(jupyterDO.getImageId())){
                if(dockerClient.isContainerRunning(jupyterDO.getImageId())){
                    log.info("检测到容器{}-{} 已经到期,自动关闭。",jupyterDO.getJupyterName(),jupyterDO.getImageId());
                    dockerClient.stopAndRemoveContainer(jupyterDO.getImageId());
                }
            }
        });
    }


    @Override
    public List<JupyterDO> queryExpiredContainers(){
        List<JupyterDO> jupyterList = jupyterMapper.selectList(new QueryWrapper<JupyterDO>()
                .le("auto_close_time", DateUtils.getDateAfterMinutes(5))
                .gt("auto_close_time", new Date())
        );
        return jupyterList;
    }


}